home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’92 / StickyClick 2.0 ƒ / StickyClick v2.0d1 ƒ / StickyClick.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-18  |  12.2 KB  |  630 lines  |  [TEXT/KAHL]

  1. #define DEBUG
  2.  
  3. #include <Traps.h>
  4. #include <GenericPatch.h>
  5. #include <OSUtils.h>
  6. #include <stddef.h>
  7. #include <Extension.h>
  8. #include <Utils.h>
  9. #include <Exceptions.h>
  10. #include "Patches.h"
  11. #include <LoMem2.h>
  12.  
  13. // <menus.h>
  14.  
  15. Point Mouse : 0x830;
  16.  
  17. static PostEventPatch *thePostEventPatch;
  18. static ButtonPatch *theButtonPatch;
  19. static GetMousePatch *theGetMousePatch;
  20. static EventAvailPatch *theEventAvailPatch;
  21.  
  22. static short theMenuSelect;
  23. static Rect theMenuBarRect;
  24.  
  25. static short _max(short a, short b)
  26. {
  27.     if (a > b)
  28.         return a;
  29.  
  30.     return b;
  31. }
  32.  
  33. static Boolean IsTimely(long lastTime)
  34. {
  35.     return (Ticks - (DoubleTime >> 1)) <= lastTime;
  36. }
  37.  
  38. static Boolean IsAround(Point p1, Point p2)
  39. {
  40.     Rect r1, r2, rToss;
  41.  
  42.     r1.top = p1.v - 2;
  43.     r1.bottom = p1.v + 2;
  44.     r1.left = p1.h - 2;
  45.     r1.right = p1.h + 2;
  46.     
  47.     r2.top = p2.v - 2;
  48.     r2.bottom = p2.v + 2;
  49.     r2.left = p2.h - 2;
  50.     r2.right = p2.h + 2;
  51.     
  52.     return SectRect(&r1, &r2, &rToss);
  53. }
  54.  
  55. static Boolean MouseInMenubar()
  56. {
  57.     return PtInRect(Mouse, &theMenuBarRect);
  58. }
  59.  
  60. #define cEsc        0x1b
  61. #define cPeriod        0x2e
  62. #define cEnter        0x03
  63. #define cReturn        0x0d
  64. #define cTab        0x09
  65.  
  66. #define cUpArrow    0x1e
  67. #define cDownArrow    0x1f
  68. #define cLeftArrow    0x1c
  69. #define cRightArrow 0x1d
  70. #define cSpace        0x20
  71.  
  72. #define CMDKEY         ((Keys1 & 0x00008000) != 0)
  73. #define OPTKEY        ((Keys1 & 0x00000004) != 0)
  74. #define SFTKEY        ((Keys1 & 0x00000001) != 0)
  75. #define CTLKEY        ((Keys1 & 0x00000008) != 0)
  76.  
  77. static void ProcessKeyDown(short c)
  78. {
  79.     switch (c & 0x00ff) {
  80.         case cPeriod:
  81.             if (! CMDKEY)
  82.                 break;
  83.             // else, fall through
  84.  
  85.         case cEsc:
  86.             theEventAvailPatch->SetStickyEvent(eStopBad);
  87.             break;
  88.  
  89.         case cEnter:
  90.         case cReturn:
  91.             if (! CTLKEY)
  92.                 theEventAvailPatch->SetStickyEvent(eStopGood);
  93.             break;
  94.  
  95.         case cTab:
  96.             if (! SFTKEY)
  97.                 theEventAvailPatch->SetStickyEvent(eNextMenu);
  98.             else
  99.                 theEventAvailPatch->SetStickyEvent(ePrevMenu);
  100.             break;
  101.             
  102.         case cRightArrow:
  103.             if (CMDKEY)
  104.                 theEventAvailPatch->SetStickyEvent(eLastMenu);
  105.             else
  106.                 theEventAvailPatch->SetStickyEvent(eNextMenu);
  107.             break;
  108.  
  109.         case cLeftArrow:
  110.             if (CMDKEY)
  111.                 theEventAvailPatch->SetStickyEvent(eFirstMenu);
  112.             else
  113.                 theEventAvailPatch->SetStickyEvent(ePrevMenu);
  114.             break;
  115.             
  116.         case cUpArrow:
  117.             theEventAvailPatch->SetStickyEvent(ePrevItem);
  118.             break;
  119.  
  120.         case cSpace:
  121.         case cDownArrow:
  122.             theEventAvailPatch->SetStickyEvent(eNextItem);
  123.             break;
  124.     }
  125. }
  126.  
  127. /******************************************************************************************/
  128.  
  129. ButtonPatch::ButtonPatch()
  130. {
  131.     GenericPatch::InitGenericPatch(_Button, offsetof(ButtonPatchParms, itsResult));
  132.     Install();
  133.     Switch(ePatchOff);
  134. }
  135.  
  136. void ButtonPatch::Behavior()
  137. {
  138.     reg ButtonPatchParms *parms = (ButtonPatchParms *) itsFrame->parameters;
  139.     parms->itsResult = -1;
  140.     AbortTrap();
  141. }
  142.  
  143. /******************************************************************************************/
  144.  
  145. PostEventPatch::PostEventPatch()
  146. {
  147.     GenericPatch::InitGenericPatch(_PostEvent, 0);
  148.     Install();
  149.     itsClick = eInitialClick;
  150. }
  151.  
  152. long PostEventPatch::FakeNavigation()
  153. {
  154.     Point zeroPt;
  155.     itsClick = eSecondClick;
  156.     theEventAvailPatch->SetupNavigation();
  157.     theButtonPatch->Switch(ePatchOn);
  158.     return MenuSelect(Mouse);
  159. }
  160.  
  161. void PostEventPatch::Behavior()
  162. {
  163.     Switch(ePatchOff);
  164.  
  165.     switch ((short) itsFrame->ra0) {
  166.         case mouseUp:
  167.             if ((itsClick == eFirstMouseUp)
  168.              && IsAround(Mouse, itsLastMouse) 
  169.              && IsTimely(itsLastTime) 
  170.              && ((theMenuSelect > 0) || MouseInMenubar())) {
  171.                 theButtonPatch->Switch(ePatchOn);
  172.                 AbortTrap();
  173.                 itsFrame->rd0 = noErr;
  174.                 itsClick = eSecondClick;
  175.             } else {
  176.                 itsClick = eInitialClick;
  177.             }
  178.             itsLastMouse = Mouse;
  179.             break;
  180.  
  181.         case mouseDown:
  182.             if (itsClick == eInitialClick)
  183.                 itsClick = eFirstMouseUp;
  184.             else if (itsClick == eSecondClick) {
  185.                 AbortTrap();
  186.                 itsFrame->rd0 = noErr;
  187.                 theButtonPatch->Switch(ePatchOff);
  188.                 itsClick = eInitialClick;
  189.             }
  190.             itsLastTime = Ticks;
  191.             itsLastMouse = Mouse;
  192.             break;
  193.  
  194.         case keyDown:
  195.             if (itsClick == eSecondClick) {
  196.                 ProcessKeyDown(itsFrame->rd0 & 0xffff);
  197.                 AbortTrap();
  198.             }
  199.             break;
  200.     }
  201.  
  202.     Switch(ePatchOn);
  203. }
  204.  
  205. /******************************************************************************************/
  206.  
  207. MenuSelectPatch::MenuSelectPatch()
  208. {
  209.     GenericPatch::InitGenericPatch(_MenuSelect, offsetof(MenuSelectParameters, itsResult));
  210.     Install();
  211. }
  212.  
  213. void MenuSelectPatch::Behavior()
  214. {    
  215. #if 0
  216.     reg MenuSelectParameters* params = (MenuSelectParameters*)itsFrame->parameters;
  217.     reg MenuSelectProcPtr oldMenuSelect = (MenuSelectProcPtr) itsOld;
  218.  
  219.     theMenuSelect++;
  220.  
  221.     params->itsResult = (*oldMenuSelect)(params->itsStartPt);
  222.     AbortTrap();
  223.     
  224.     theMenuSelect--;
  225. #endif
  226. }
  227.  
  228. /******************************************************************************************/
  229.  
  230. MenuKeyPatch::MenuKeyPatch()
  231. {
  232.     GenericPatch::InitGenericPatch(_MenuKey, offsetof(MenuKeyParameters, itsResult));
  233.     Install();
  234. }
  235.  
  236. void MenuKeyPatch::Behavior()
  237. {
  238.     reg MenuKeyParameters* params = (MenuKeyParameters*) itsFrame->parameters;
  239.     
  240.     if (params->itsKey == cSpace) {
  241.         AbortTrap();
  242.         params->itsResult = thePostEventPatch->FakeNavigation();
  243.     }
  244. }
  245.  
  246. /******************************************************************************************/
  247.  
  248. PopupMenuSelectPatch::PopupMenuSelectPatch()
  249. {
  250.     GenericPatch::InitGenericPatch(_PopUpMenuSelect, offsetof(PopupMenuSelectParameters, itsResult));
  251.     Install();
  252. }
  253.  
  254. void PopupMenuSelectPatch::Behavior()
  255. {
  256.     reg PopupMenuSelectParameters* params = (PopupMenuSelectParameters*)itsFrame->parameters;
  257.     reg PopupMenuSelectProcPtr oldMenuSelect = (PopupMenuSelectProcPtr) itsOld;
  258.  
  259.     theMenuSelect++;
  260.  
  261. #if !__option(a4_globals)
  262.         {
  263.             long oldA5 = SetA5((long) itsFrame->ra5);
  264. #endif
  265.  
  266.             params->itsResult = (*oldMenuSelect)(params->itsMenuHandle, params->itsTop, params->itsLeft, params->itsItem);
  267.  
  268. #if !__option(a4_globals)
  269.             (void) SetA5(oldA5);
  270.         }
  271. #endif
  272.  
  273.     AbortTrap();
  274.  
  275.     theMenuSelect--;
  276. }
  277.  
  278. ////////////////////////////////////////////////////////////////////////////////////////////////
  279.  
  280. void GetMousePatch::GetMousePatch()
  281. {
  282.     GenericPatch::InitGenericPatch(_GetMouse, offsetof(GetMouseParameters, dummy));
  283.     Install();
  284.     Switch(ePatchOff);
  285. }
  286.  
  287. void GetMousePatch::Behavior()
  288. {
  289.     *(long*) &((GetMouseParameters*) (itsFrame->parameters))->itsPoint = 0;
  290.     AbortTrap();
  291. }
  292.  
  293. /******************************************************************************************/
  294.  
  295. void EventAvailPatch::EventAvailPatch()
  296. {
  297.     itsFromStart = false;
  298.     GenericPatch::InitGenericPatch(_EventAvail, offsetof(EventAvailParameters, itsResult));
  299.     Install();
  300. }
  301.  
  302. // the number of real menus:
  303. // real menus in the menu list (that are in the menubar)
  304. // have a leftEdge > 0
  305. short EventAvailPatch::NumRealMenus()
  306. {
  307.     reg short ctMenus = ((**theMenuList).itemOffset - 6) / sizeof(PerMenu);
  308.     reg short ixMenu;
  309.     reg PerMenu *p;
  310.     reg short actualMenus = 0;
  311.     
  312.     p = (**theMenuList).permenu;
  313.  
  314.     for (ixMenu = 0;  ixMenu <= ctMenus;  ixMenu++, ++p) {
  315.         if (p->itsLeftEdge > 0)
  316.             ++actualMenus;
  317.     }
  318.  
  319.     return actualMenus;
  320. }
  321.  
  322. Boolean EventAvailPatch::FindMenuTitleLoc(Boolean next, Point *loc)
  323. {
  324.     short ctMenus = NumRealMenus();
  325.     short ixMenu;
  326.     reg PerMenu *p;
  327.     
  328.     p = (**theMenuList).permenu;
  329.  
  330.     for (ixMenu = 0;  ixMenu < ctMenus;  ixMenu++, ++p) {
  331.         if (p->itsHandle == nil)
  332.             continue;
  333.             
  334.         if ((**p->itsHandle).menuID == (short) (MenuDisable >> 16)) 
  335.             break;
  336.     }
  337.  
  338.     if (ixMenu >= ctMenus) // how to handle error?
  339.         return false;
  340.  
  341.     do {
  342.         if (next) {
  343.             if (ixMenu == (ctMenus - 1))
  344.                 ixMenu = 0;
  345.             else
  346.                 ixMenu++;
  347.         } else {
  348.             if (ixMenu == 0)
  349.                 ixMenu = ctMenus - 1;
  350.             else
  351.                 --ixMenu;
  352.         }
  353.  
  354. #ifdef DEBUG
  355.         if (ixMenu < 0)
  356.             DebugStr("\pixMenu < 0!!!");
  357.         if (ixMenu >= ctMenus)
  358.             DebugStr("\pixMenu > xx??!!");
  359. #endif
  360.  
  361.         p = &(**theMenuList).permenu[ixMenu];
  362.     } while ((**p->itsHandle).menuID == 0xb020);
  363.  
  364.     CalcMenuSize(p->itsHandle);                // note that p may have moved!
  365.     p = &(**theMenuList).permenu[ixMenu];
  366.  
  367.     loc->v = MBarHeight >> 1;
  368.     loc->h = p->itsLeftEdge + 8;
  369.  
  370.     return true;
  371. }
  372.  
  373. Boolean EventAvailPatch::FindFirstMenuLoc(Point *loc)
  374. {
  375.     reg PerMenu *p;
  376.     
  377.     p = (**theMenuList).permenu;
  378.  
  379.     CalcMenuSize(p->itsHandle);                // note that p may have moved!
  380.  
  381.     p = (**theMenuList).permenu;
  382.  
  383.     loc->v = MBarHeight >> 1;
  384.     loc->h = p->itsLeftEdge + 8;
  385.  
  386.     return true;
  387. }
  388.  
  389. Boolean EventAvailPatch::FindLastMenuLoc(Point *loc)
  390. {
  391.     short ctMenus = NumRealMenus();
  392.     reg PerMenu *p;
  393.     
  394.     p = &(**theMenuList).permenu[ctMenus-1];
  395.  
  396.     CalcMenuSize(p->itsHandle);                // note that p may have moved!
  397.  
  398.     p = &(**theMenuList).permenu[ctMenus-1];
  399.  
  400.     loc->v = MBarHeight >> 1;
  401.     loc->h = p->itsLeftEdge + 8;
  402.  
  403.     return true;
  404. }
  405.  
  406. short EventAvailPatch::FindMenuItemVPos(MenuHandle hMenu, short item)
  407. {
  408.     reg short v = MBarHeight;
  409.     short ctItems;
  410.     Str255 s;
  411.     short i;
  412.  
  413.     if (item == 0)
  414.         return v - 3;
  415.     
  416.     v += 3;
  417.     
  418.     if (item == 1)
  419.         return v;
  420.  
  421.     ctItems = CountMItems(hMenu);
  422.  
  423.     for (i = 1;  i <= ctItems;  i++) {
  424.         if (i == item)
  425.             return v;
  426.  
  427.         GetItem(hMenu, i, s);
  428.         
  429.         if (s[1] == '-')
  430.             v += 13;
  431.         else
  432.             v += 18;
  433.     }
  434.     
  435.     return v;
  436. }
  437.  
  438.  
  439. Boolean EventAvailPatch::FindMenuItemLoc(Boolean next, Point *loc)
  440. {
  441.     Rect curMenuRect;
  442.     short menuID = MenuDisable >> 16;
  443.     short menuItem = MenuDisable & 0xffff;
  444.     short ctItems;
  445.     MenuHandle hMenu;
  446.     short leftEdge;
  447.     
  448.     hMenu = FindInstalledMenu(menuID, &leftEdge);
  449.     
  450.     if (hMenu == nil)
  451.         return false;
  452.     
  453.     ctItems = CountMItems(hMenu);
  454.     
  455.     if (menuItem == 1 && ctItems == 1)    // special case, get off menu
  456.         menuItem = 0;
  457.     else {
  458.         Str255 menuString;
  459.         short tries;
  460.         
  461.         for (tries = 0;  tries < ctItems;  tries++) {
  462.             if (next) {
  463.                 if (menuItem == ctItems)
  464.                     menuItem = 1;
  465.                 else
  466.                     ++menuItem;
  467.             } else {
  468.                 if (menuItem <= 1)
  469.                     menuItem = ctItems;
  470.                 else
  471.                     --menuItem;
  472.             }            
  473.             if ((((**hMenu).enableFlags & (1 << menuItem)) != 0)) {    // item enabled?
  474.                 GetItem(hMenu, menuItem, menuString);
  475.                 if (menuString[1] != '-')                            // and not a -? break!
  476.                     break;
  477.             }
  478.         }
  479.         
  480.         if (tries == ctItems) {
  481.             SysBeep(1);
  482.             return false;
  483.         }
  484.     }
  485.  
  486.     loc->h = leftEdge + 8;
  487.     loc->v = FindMenuItemVPos(hMenu, menuItem);
  488.  
  489.     return true;
  490. }
  491.  
  492. static void DumpScreen()
  493. {
  494. #if 0
  495.     Handle hProc = GetResource('FKEY', 3);
  496.     if (hProc == nil) {
  497.         SysBeep(1);
  498.         return;
  499.     }
  500.     HNoPurge(hProc);
  501.     asm {
  502.         move.l    hProc,a0
  503.         move.l    (a0),a0
  504.         jsr        (a0)
  505.     }
  506.     HPurge(hProc);
  507. #endif
  508. }
  509.  
  510. void EventAvailPatch::Behavior()
  511. {
  512.     Point loc;
  513.     reg StickyEvent eve = itsEvent;
  514.  
  515.     itsEvent = eNullEvent;    // consume
  516.     
  517.     switch (eve) {
  518.         case eStart:
  519.             break;
  520.  
  521.         case eStopBad:
  522.             DumpScreen();
  523.             theGetMousePatch->Switch(ePatchOn);
  524.             itsEvent = eStopCleanup;
  525.             break;
  526.  
  527.         case eStopGood:
  528.             itsEvent = eStopCleanup;
  529.             break;
  530.  
  531.         case eStopCleanup:
  532.             theGetMousePatch->Switch(ePatchOff);    // irregardless
  533.             (void) PostEvent(mouseUp, 0);
  534.             
  535.             if (itsFromStart)
  536.                 SetMouse(itsInitialLoc);
  537.     
  538.             itsFromStart = false;
  539.             break;
  540.  
  541.         case eNextMenu:
  542.         case ePrevMenu:
  543.             if (FindMenuTitleLoc(eve == eNextMenu, &loc))
  544.                 SetMouse(loc);
  545.             break;
  546.  
  547.         case eFirstMenu:
  548.             if (FindFirstMenuLoc(&loc))
  549.                 SetMouse(loc);
  550.             break;
  551.         
  552.         case eLastMenu:
  553.             if (FindLastMenuLoc(&loc))
  554.                 SetMouse(loc);
  555.             break;
  556.  
  557.         case eNextItem:
  558.         case ePrevItem:
  559.             if (FindMenuItemLoc(eve == eNextItem, &loc))
  560.                 SetMouse(loc);
  561.             break;
  562.     }
  563. }
  564.  
  565. void EventAvailPatch::SetupNavigation()
  566. {
  567.     Point loc;
  568.     itsEvent = eStart;
  569.     itsFromStart = true;
  570.     GetMouse(&itsInitialLoc);
  571.     (void) FindFirstMenuLoc(&loc);
  572.     SetMouse(loc);
  573. }
  574.  
  575. void EventAvailPatch::SetStickyEvent(StickyEvent eve)
  576. {
  577.     itsEvent = eve;
  578. }
  579.  
  580. /******************************************************************************************/
  581.  
  582. void Install()
  583. {
  584.     theMenuSelect = 0;
  585.     
  586.     try {
  587.         // if extension succeeds, show happy icon.
  588.         SysEnvRec environment;            // machine configuration.
  589.         
  590.         // find out what kind of machine this is.
  591.         SysEnvirons(curSysEnvVers, &environment);
  592.  
  593.         thePostEventPatch = new PostEventPatch;
  594.         theButtonPatch = new ButtonPatch;
  595. //        new MenuSelectPatch;
  596.         new MenuKeyPatch;
  597.         new PopupMenuSelectPatch;
  598.         theGetMousePatch = new GetMousePatch;
  599.         theEventAvailPatch = new EventAvailPatch;
  600.  
  601.         if (environment.hasColorQD)
  602.             theMenuBarRect.right = (**MainDevice).gdRect.right;
  603.         else {
  604.             RgnHandle r = GetGrayRgn();
  605.             if (r == nil)
  606.                 DebugStr("\pnil grayrgn?");
  607.             theMenuBarRect.right = (**r).rgnBBox.right - (**r).rgnBBox.left;
  608.         }
  609.  
  610.         theMenuBarRect.left = 0;
  611.         theMenuBarRect.top = 0;
  612.         theMenuBarRect.bottom = 0x14;
  613.  
  614.         ShowIconFamily(128);
  615.     } /* try */
  616.  
  617.     catch {
  618.         // extension failed, show sad icon.
  619.         ShowIconFamily(129);
  620.         throw(theException);
  621.     }
  622. }
  623.  
  624. // called when system is shutdown.
  625.  
  626. void Remove()
  627. {    
  628.     Patch::RemoveAll();
  629. }
  630.